package me.sealer.ssm.service.impl;

import com.github.pagehelper.PageHelper;
import me.sealer.ssm.constant.ErrorCodeConstant;
import me.sealer.ssm.constant.PageConstant;
import me.sealer.ssm.exception.SsmException;
import me.sealer.ssm.mapper.MenuMapper;
import me.sealer.ssm.model.Menu;
import me.sealer.ssm.model.MenuExample;
import me.sealer.ssm.page.PageResult;
import me.sealer.ssm.service.MenuService;
import me.sealer.ssm.utils.PageUtil;
import me.sealer.ssm.utils.StringUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 功能描述： $ MenuServiceImpl
 *
 * @author sealer
 * @email 1178884049@qq.com
 * @date 2020年01月06日 09时10分21秒
 */
@Service
public class MenuServiceImpl implements MenuService {
    private final MenuMapper menuMapper;

    public MenuServiceImpl(MenuMapper menuMapper) {
        this.menuMapper = menuMapper;
    }

    @Override
    public List<Menu> queryAllMenus() {
        return menuMapper.selectByExample(null);
    }

    @Override
    public List<Menu> queryFirstMenus() {
        MenuExample menuExample = new MenuExample();
        MenuExample.Criteria criteria = menuExample.createCriteria();
        criteria.andParentMenuIdEqualTo(0);

        return menuMapper.selectByExample(menuExample);
    }

    @Override
    public PageResult<Menu> queryMenusPage(String alias, String type, Integer level, int pageNo, int pageSize) {
        pageSize = Math.min(pageSize, PageConstant.MAX_PAGE_SIZE);

        MenuExample menuExample = generateExampleIfNonNull(alias, type, level);
        int count = menuMapper.countByExample(menuExample);

        PageHelper.startPage(pageNo, pageSize);
        List<Menu> menus = menuMapper.selectByExample(menuExample);

        return PageUtil.generatePageResult(pageNo, pageSize, menus, count);
    }

    @Override
    public List<Menu> queryMenus(String alias, String type, Integer level) {
        MenuExample menuExample = generateExampleIfNonNull(alias, type, level);
        return menuMapper.selectByExample(menuExample);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertOne(Menu menu) {
        int maxId;
        MenuExample menuExample = new MenuExample();

        Integer parentMenuId = menu.getParentMenuId();
        menuExample.createCriteria().andParentMenuIdEqualTo(parentMenuId);
        List<Menu> menus = menuMapper.selectByExample(menuExample);

        if (menus.size() == 0) {
            maxId = parentMenuId * 100;
        } else {
            Optional<Integer> maxOption = menus.parallelStream().map(Menu::getId).max(Integer::compareTo);
            if (!maxOption.isPresent()) {
                throw new SsmException(ErrorCodeConstant.SYSTEM_UNKNOWN_ERROR);
            }
            maxId = maxOption.get();
        }

        menu.setId(maxId + 1);

        menuMapper.insertSelective(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenu(Integer id, String alias, String type) {
        MenuExample menuExample = generateExampleAnd(id, alias, type);
        int count = menuMapper.countByExample(menuExample);
        if (count == 0) {
            throw new SsmException(ErrorCodeConstant.MENU_NOT_EXISTS);
        }

        // delete self
        menuMapper.deleteByPrimaryKey(id);

        // delete sub menus
        MenuExample deleteExample = new MenuExample();
        deleteExample.createCriteria().andParentMenuIdEqualTo(id);
        menuMapper.deleteByExample(deleteExample);
    }

    private MenuExample generateExampleIfNonNull(String alias, String type, Integer level) {
        MenuExample menuExample = new MenuExample();
        MenuExample.Criteria criteria = menuExample.createCriteria();

        if (!StringUtil.isNullOrEmpty(alias)) {
            criteria.andAliasLike("%" + alias + "%");
        }

        if (!StringUtil.isNullOrEmpty(type)) {
            criteria.andTypeEqualTo(type);
        }

        if (Objects.nonNull(level)) {
            criteria.andIdBetween((int) Math.pow(100, level - 1), (int) Math.pow(100, level));
        }

        return menuExample;
    }

    private MenuExample generateExampleAnd(Integer id, String alias, String type) {
        MenuExample menuExample = new MenuExample();
        menuExample.createCriteria()
                .andIdEqualTo(id)
                .andAliasLike("%" + alias + "%")
                .andTypeEqualTo(type);

        return menuExample;
    }
}
